package com.ydy.zuul.filter;

import com.auth0.jwt.interfaces.Claim;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import com.ydy.common.constant.Constant;
import com.ydy.common.model.SysLog;
import com.ydy.common.util.CodeUtil;
import com.ydy.common.util.JsonUtil;
import com.ydy.common.util.JwtTokenUtil;
import com.ydy.common.util.RedisUtil;
import com.ydy.zuul.service.ZuulService;
import com.ydy.zuul.util.ZuulParameterUtil;
import eu.bitwalker.useragentutils.UserAgent;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.codehaus.jackson.type.TypeReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.*;

@Component
public class RequestZuulFilter extends ZuulFilter {

    @Autowired
    private ZuulService zuulService;

    @Autowired
    private RedisTemplate<Serializable, Object> redisTemplate;

    private static Logger LOGGER = LoggerFactory.getLogger(RequestZuulFilter.class);

    /**
     * 网关类型
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 网关优先级
     */
    @Override
    public int filterOrder() {
        return 0;
    }

    /**
     * 是否开启
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    /**
     * 过滤逻辑
     */
    @Override
    public Object run() throws ZuulException {

        LocalDateTime currentTime = LocalDateTime.now();

        RequestContext requestContext = RequestContext.getCurrentContext();
        HttpServletRequest request = requestContext.getRequest();
        UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader(Constant.USER_AGENT));
        //获取客户端操作系统
        String os = userAgent.getOperatingSystem().getName();
        //获取客户端浏览器
        String browser = userAgent.getBrowser().getName();
        //获得ip
        String ip = request.getHeader(Constant.HOST);
        //請求地址
        String url = request.getRequestURL().toString();
        //請求頭
        String header = request.getHeader(Constant.AUTHORIZATION);

        String userCode = new String();
        String userName = new String();
        //请求参数
        Map<String,Object> paramMap = ZuulParameterUtil.getRequestParams(requestContext);

        if(!url.contains(Constant.LOGIN)) {
            //请求uri
            String uri = request.getRequestURI();

            if(!uri.toLowerCase().contains(Constant.DOWNLOAD)){
                if(StringUtils.isEmpty(header)){
                    LOGGER.warn("token is empty");
                    requestContext.setSendZuulResponse(false);
                    requestContext.setResponseStatusCode(401);
                    return null;
                }else{
                    //验证token
                    checkToken(header,userCode,userName,requestContext,uri);

                    userCode = MapUtils.getString(requestContext,Constant.USER_CODE);
                    userName = MapUtils.getString(requestContext,Constant.USER_NAME);
                }
            }
        }
        LOGGER.info("用户{}，在{}，使用{}操作系统，{}浏览器，发起{}请求，请求头为{}，请求参数为{}",
                userCode,currentTime,os,browser,url,header,paramMap);
        Date date = new Date();
        SysLog sysLog = new SysLog(CodeUtil.getCode(),userCode,userName,os,browser,
                ip,date,url,header,JsonUtil.getJson(paramMap));
        zuulService.insertSysLog(sysLog);
        return null;
    }

    /**
     * 校验token
     * @Title: checkToken
     * @author: dy.yin 2021/4/8 13:08
     * @param: [header, userCode, userName, requestContext]
     * @return: com.netflix.zuul.context.RequestContext
     * @throws
     */
    private RequestContext checkToken(String header, String userCode, String userName, RequestContext requestContext,String uri) {
        Map<String, Claim> map = JwtTokenUtil.verifyToken(header);
        if(!map.containsKey(Constant.USER_CODE)){
            LOGGER.warn("token{}错误",header);
            requestContext.setSendZuulResponse(false);
            requestContext.setResponseStatusCode(401);
        }else{
            Claim claimCode = map.get(Constant.USER_CODE);
            Claim claimName = map.get(Constant.USER_NAME);
            userCode = claimCode.asString();
            userName = claimName.asString();

            requestContext.set(Constant.USER_CODE,userCode);
            requestContext.set(Constant.USER_NAME,userName);

            //验证是否过期
            RedisUtil redisUtil = new RedisUtil(redisTemplate);
            boolean isOverdue = redisUtil.hasKey("token:" + userCode + header);
            if(!isOverdue){
                LOGGER.warn("token{}已过期",header);
                requestContext.setSendZuulResponse(false);
                requestContext.setResponseStatusCode(401);
            }else{
                //校验权限
                Set<String> permissions = JsonUtil.jsonToObject(String.valueOf(redisUtil.get("permission:" + userCode + header)),new TypeReference<TreeSet<String>>(){});

                //获得时间
                Long timeOutTime = Long.valueOf(String.valueOf(redisUtil.get(Constant.REDIS_TIMEOUT_TIME)));
                //是否校验权限
                String ifCheckPermission = String.valueOf(redisUtil.get(Constant.CHECK_PERMISSION_SWITCH));

                //不校验权限
                if("true".equals(ifCheckPermission) && !permissions.contains(uri)){
                    requestContext.setSendZuulResponse(false);
                    requestContext.setResponseStatusCode(403);
                }

                redisUtil.expire("token:" + userCode + header, timeOutTime);
                redisUtil.expire("permission:" + userCode + header,timeOutTime);
            }
        }
        return requestContext;
    }
}
